﻿/*
Copyright (c) 2013, Marc Tanti
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the www.marctanti.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MARIA.program.statement;
using MARIA.level;
using MARIA.program.statementfactory;

namespace MARIA
{
    public partial class ProgramTreeControl : UserControl
    {

        private Level level;
        private Statement lastSelectedStatement;

        public bool Executing
        {
            get
            {
                return executing;
            }
            set
            {
                executing = value;

                Statement selectedNode = GetSelectedStatement();
                if (selectedNode != null)
                {
                    pnlStatementEditor.Controls.Clear();
                    if (executing)
                    {
                        selectedNode.BackColor = Color.Yellow;
                    }
                    else
                    {
                        RecursiveHighlightColours(selectedNode);
                        selectedNode.SetEditPanel(pnlStatementEditor);
                    }
                }
            }
        }
        private bool executing;

        public ProgramTreeControl()
        {
            InitializeComponent();
            
            executing = false;

            ImageList imageList = new ImageList();
            foreach (StatementFactory statement in Cache.Instance.Statements)
            {
                imageList.Images.Add(statement.StatementTextualRepresentation, statement.StatementImage);
            }
            tvwProgramTree.ImageList = imageList;
        }

        public void SetLevel(Level level)
        {
            this.level = level;
            tvwProgramTree.Nodes.Clear();
            tvwProgramTree.Nodes.Add(level.Program);
            tvwProgramTree.ExpandAll();
            SetSelectedStatement(level.Program);
            RecursiveGiveImageKey(level.Program);
            RecursiveClearColours(level.Program);
        }

        public Statement GetProgram()
        {
            return level.Program;
        }

        public Statement GetSelectedStatement()
        {
            return (Statement)tvwProgramTree.SelectedNode;
        }

        public void SetSelectedStatement(Statement statement)
        {
            tvwProgramTree.SelectedNode = statement;
        }

        public void MakeUneditable()
        {
            tvwProgramTree.Enabled = false;
        }

        public void MakeEditable()
        {
            tvwProgramTree.Enabled = true;
        }

        private bool CanInsert(Statement parent, Statement child)
        {
            return parent != null && parent.Parent != null && parent.CanAdd(child);
        }

        private bool CanRemove(Statement statement)
        {
            return statement != null && statement.Parent != null && statement.Parent.Parent != null && ((Statement)statement.Parent).CanRemove(statement);
        }

        private void InsertStatement(Statement parent, int index, Statement child)
        {
            if (CanInsert(parent, child))
            {
                RecursiveGiveImageKey(child);
                RecursiveClearColours(child);
                parent.Nodes.Insert(index, child);
                SetSelectedStatement(child);
            }
            else
            {
                MessageBox.Show("This statement can't go there.");
            }
        }

        public void RemoveSelected()
        {
            Statement selectedStatement = GetSelectedStatement();
            if (CanRemove(selectedStatement))
            {
                selectedStatement.Remove();
            }
            else
            {
                MessageBox.Show("This statement can't be removed from there.");
            }
        }

        public void InsertAboveSelected(Statement statement)
        {
            Statement selected = GetSelectedStatement();
            if (selected != null && selected.Parent != null)
            {
                InsertStatement((Statement)selected.Parent, selected.Index, statement);
            }
        }

        public void InsertIntoSelected(Statement statement)
        {
            Statement selected = GetSelectedStatement();
            InsertStatement(selected, selected.Nodes.Count - 1, statement);
        }

        public void InsertBelowSelected(Statement statement)
        {
            Statement selected = GetSelectedStatement();
            if (selected != null && selected.Parent != null)
            {
                InsertStatement((Statement)selected.Parent, selected.Index + 1, statement);
            }
        }

        private void RecursiveGiveImageKey(Statement statement)
        {
            statement.ImageKey = statement.GetFactory().StatementTextualRepresentation;
            statement.SelectedImageKey = statement.GetFactory().StatementTextualRepresentation;
            foreach (TreeNode child in statement.Nodes)
            {
                RecursiveGiveImageKey((Statement)child);
            }
        }

        private void RecursiveClearColours(Statement statement)
        {
            statement.BackColor = Color.Transparent;
            statement.ForeColor = Color.Black;
            foreach (TreeNode child in statement.Nodes)
            {
                RecursiveClearColours((Statement)child);
            }
        }

        private void RecursiveHighlightColours(Statement statement)
        {
            _RecursiveHighlightColours(statement);
            statement.BackColor = Color.Blue;
            statement.ForeColor = Color.White;
        }

        private void _RecursiveHighlightColours(Statement statement)
        {
            statement.BackColor = Color.LightBlue;
            statement.ForeColor = Color.White;
            foreach (TreeNode child in statement.Nodes)
            {
                _RecursiveHighlightColours((Statement)child);
            }
        }

        private void tvwProgramTree_AfterSelect(object sender, TreeViewEventArgs e)
        {
            Statement selectedNode = (Statement)e.Node;

            if (lastSelectedStatement != null)
            {
                RecursiveClearColours(lastSelectedStatement);
            }

            pnlStatementEditor.Controls.Clear();
            if (executing)
            {
                selectedNode.BackColor = Color.Yellow;
            }
            else
            {
                RecursiveHighlightColours(selectedNode);
                selectedNode.SetEditPanel(pnlStatementEditor);
            }
            if (selectedNode.Parent != null)
            {
                selectedNode.Parent.Expand();
                selectedNode.EnsureVisible();
            }

            lastSelectedStatement = selectedNode;
        }

        private void tvwProgramTree_ItemDrag(object sender, ItemDragEventArgs e)
        {
            Statement statement = (Statement)e.Item;
            if (CanRemove(statement))
            {
                statement.Remove();
                DoDragDrop(new DragProxy(statement), DragDropEffects.All);
            }
        }

        private void tvwProgramTree_DragOver(object sender, DragEventArgs e)
        {
            Statement mouseoverStatement = (Statement)(tvwProgramTree.GetNodeAt(tvwProgramTree.PointToClient(new Point(e.X, e.Y))));
            if (mouseoverStatement != null && e.Data.GetDataPresent(typeof(DragProxy)))
            {
                Statement draggedStatement = ((DragProxy)e.Data.GetData(typeof(DragProxy))).dragged;

                if (e.KeyState == (1 | 4)) //Left mouse button + shift
                {
                    SetSelectedStatement(mouseoverStatement);
                    if (CanInsert(mouseoverStatement, draggedStatement))
                    {
                        e.Effect = DragDropEffects.All;
                    }
                    else
                    {
                        e.Effect = DragDropEffects.None;
                    }
                }
                else if (e.KeyState == 1 || e.KeyState == (1 | 32)) //Left mouse button or left mouse button + alt
                {
                    SetSelectedStatement((Statement)mouseoverStatement.Parent);
                    if (CanInsert((Statement)mouseoverStatement.Parent, draggedStatement))
                    {
                        e.Effect = DragDropEffects.All;
                    }
                    else
                    {
                        e.Effect = DragDropEffects.None;
                    }
                }
            }
        }

        private void tvwProgramTree_DragDrop(object sender, DragEventArgs e)
        {
            Statement draggedStatement = ((DragProxy)e.Data.GetData(typeof(DragProxy))).dragged;
            Statement mouseoverStatement = (Statement)(tvwProgramTree.GetNodeAt(tvwProgramTree.PointToClient(new Point(e.X, e.Y))));

            if (mouseoverStatement != null)
            {
                if (e.KeyState == 4) //shift
                {
                    InsertStatement(mouseoverStatement, mouseoverStatement.Nodes.Count - 1, draggedStatement);
                }
                else if (e.KeyState == 32) //alt
                {
                    InsertStatement((Statement)mouseoverStatement.Parent, mouseoverStatement.Index, draggedStatement);
                }
                else
                {
                    InsertStatement((Statement)mouseoverStatement.Parent, mouseoverStatement.Index + 1, draggedStatement);
                }
            }
        }

    }
}
